#light

namespace CGraph.Generic

open System.Collections.Generic

type ReverseListEnumerator<'T>(m_list:List<'T>) =
    let mutable m_index = -1
    interface System.Collections.IEnumerator with
        member this.Current with get() = (this :> IEnumerator<'T>).Current :> obj
        member this.Reset() = ignore( m_index <- 0 )
        member this.MoveNext() = 
            if m_index >= m_list.Count - 1
                then false
                else
                    m_index <- 1 + m_index;
                    true
        
    interface IEnumerator<'T> with
        member this.Current with get() = m_list.Item(m_list.Count - m_index - 1)
        member this.Dispose() = ()
                
type ReverseListEnumerable<'T>(m_list:List<'T>) =
    interface System.Collections.IEnumerable with
        member this.GetEnumerator() = new ReverseListEnumerator<'T>(m_list) :> System.Collections.IEnumerator
    interface IEnumerable<'T> with
        member this.GetEnumerator() = new ReverseListEnumerator<'T>(m_list) :> IEnumerator<'T>
        
module List =
    /// Creates a list from an enumerable, where the argument could be null.
    /// The list constructor expects non-null IEnumerable and throws an exception
    /// should that criteria not be met; thus this wraps that constructor
    /// and calls another the default constructor if the argument a is null
    let create( a : System.Collections.Generic.IEnumerable<'T> ) =
        if a <> null then new System.Collections.Generic.List<'T>(a) else new System.Collections.Generic.List<'T>()
        
                        
    /// Return a reverse enumerable based on the list.
    let create_reverse_enumerable( a ) = new ReverseListEnumerable<'T>(a)
        
        
/// Represents an object with a name.
type INamedObject =
    abstract Name : string with get
    
type IDocumentedObject = 
    abstract Documentation : string with get
    
 /// Represents an object with some unique identifier.
type IIdObject =
    abstract Id : string with get
   
///Represents an abstract unit of work that may be undone.
///Essentially, represents a side effect.
type IAction = 
    ///Do the unit of work over again
    abstract Do : unit->unit
    ///Undo the unit of work.
    abstract Undo : unit->unit
    
/// Sometimes actions are mergeable in the action stream
/// This is important in the case where a user is dragging an object.
/// This may generate 1000s of actions over time, but an action list
/// should merge them so that on undo or redo only a couple actions
/// actually get run.
type IMergeable =
    /// True if this is actually mergeable
    abstract Mergeable : bool with get
    /// The merge key.  You should probably include the actual type name
    /// if you depend on casting this object to the precise type.
    /// Edge collection actions, for example, return a tuple pointing
    /// to the graph they belong to as well as their type name "EdgeCollectionAction".
    abstract MergeKey : System.Object with get
    /// Merge this action with one later in the stream.  This action will
    /// not be included in the action stream if it has been successfully merged
    /// The argument is the next action, and it will not be added to the stream.
    /// You can assume the next action has been done.
    abstract Merge : IMergeable -> bool
    
/// Some actions will represent adding/removing items
/// in various different formats.  These actions
/// should represent their specific side effects
/// in terms of items added and items removed.  Upon undo,
/// those lists should be reversed.
type ICollectionAction<'T> =
    /// List of items added to the system
    abstract AddedItems : seq<'T> with get
    /// List of items removed from the system.
    abstract RemovedItems : seq<'T> with get
    
type IPropertyAction<'T,'U> =
    abstract Owner : 'T with get
    abstract Value : 'U with get
    
/// Stream of actions.  These should merge actions that are mergeable
/// <see>IMergeableAction</see>.  They represent a list of actions.
/// Sequence order reverses depending on whether the last operation was
/// a 'do' or 'undo'.
/// during the actual operations the item may iterate backwords for undo.
type IActionList =
    interface seq<IAction>
    /// If called after an undo, it will add the action
    /// to the beginning of the list instead of the end
    abstract Add : IAction->unit
    abstract Do : unit->unit
    abstract Undo : unit->unit
    abstract OrderReversed : bool with get
    
    

/// Maintains two lists and keeps track of which
/// items need to be added or removed.  This is used
/// currently for the graph collection actions, as they
/// maintain lists of items (vertexes or edges) to add or 
/// to remove.   
type AddRemoveList<'T> =
    val m_addList : List<'T>
    val m_removeList : List<'T>
    
    new( add : System.Collections.Generic.IEnumerable<'T>, rem : System.Collections.Generic.IEnumerable<'T> ) = {
            m_addList = List.create( add )
            m_removeList = List.create( rem )
        }
    
    member s.AddList with get() = s.m_addList
    member s.RemoveList with get() = s.m_removeList
        
    member this.AddItem item =
        if this.m_removeList.Remove(item) = false && this.m_addList.Contains(item) = false
            then this.m_addList.Add( item )
            
    member this.RemoveItem item =
        if this.m_addList.Remove(item) = false && this.m_removeList.Contains( item ) = false 
            then this.m_removeList.Add( item )
        
    member this.Merge( other : AddRemoveList<'T> ) = 
        for item in other.m_removeList do
            this.RemoveItem item
        for item in other.m_addList do
            this.AddItem item
                
                
type ILogStream =
    abstract member Write : string -> unit
    abstract member WriteLine : string -> unit
    
/// You can get a lot more complicated than this, but at some
/// level, this is really all you need.
type ILogger =
    abstract member Info : ILogStream option with get
    abstract member Warning : ILogStream option with get
    abstract member Error : ILogStream option with get
    
    
/// If the enumerable supports adding objects.
type IAddable<'a> =
    abstract Add : unit -> 'a
    
/// If the enumerable supports removing objects.
type IRemovable<'a> =
    abstract Remove : 'a -> bool
    
/// If the enumerable supports moving objects.
/// -1 means end, 0 means start 
/// This is parameterized with the other argument type
/// so that you can be specific about what you are moving to.
/// but this basically moves a into list b at index int
type IMoveable<'a> =
    abstract Move : 'a * int * IMoveable<'a> -> unit
    
/// If the enumerable supports random access
/// The object should also support count.
type IRandomAccess<'a> =
    abstract ItemAt : int -> 'a
    
/// If the enumerable supports getting a count.
type ICountable<'a> =
    abstract Count : int with get
    

/// Map a set of objects keyed two ways.
type BiDirectionalMap<'a,'b>() =
    let m_forward = Dictionary<'a,'b>()
    let m_backward = Dictionary<'b,'a>()
    
    member s.AddItem (key,value) =
        m_forward.Add( key, value )
        m_backward.Add( value, key )
        
    member s.EnsureAdded (key,item) =
        let success,hdl = m_backward.TryGetValue item
        if (not success ) 
            then s.AddItem (key,item)
            
    member s.Item 
        with get key = m_forward.[key] 
        and set (key,value) =
            m_forward.[key] <- value
            m_backward.[value] <- key
    
    member s.Values
        with get value = m_backward.[value]
            
    member s.Remove key =
        let value = s.[key]
        m_forward.Remove key && 
            m_backward.Remove value
        
    member s.RemoveValue value =
        let key = s.Values value
        s.Remove key
    
    member s.ContainsKey key =
        m_forward.ContainsKey key
     
    member s.ContainsValue value =
        m_backward.ContainsKey value
        
    member s.TryGetValue key =
        m_forward.TryGetValue key
        
    member s.TryGetKey value =
        m_backward.TryGetValue value
        
    interface System.Collections.IEnumerable with
        member s.GetEnumerator() = m_forward.GetEnumerator() :> System.Collections.IEnumerator
        
    interface System.Collections.Generic.IEnumerable<'a*'b> with
        member s.GetEnumerator() = 
            let retval = 
                m_forward
                |> Seq.map( fun( pair )->pair.Key,pair.Value )
            retval.GetEnumerator()
            
    member s.ValueEnumerable 
        with get = 
            m_backward
            |> Seq.map( fun (pair)->pair.Key,pair.Value )
 
//Increment function.
type IncrementFunc<'a> = unit->'a
   
/// Represents a sequence of incrementing (or whatever) objects
type Incrementor<'a>(m_inc:'a->'a,start:'a) =
    let mutable m_current = start
    member s.Current = m_current
    member s.Next =
        m_current <- m_inc(m_current)
        m_current
 

/// Map an integer handle to an object
type HandleMap<'a>() =
    let m_biMap = BiDirectionalMap<int,'a>()
    let mutable m_topHandle = Incrementor<int>((fun item -> item + 1),1)
    
    member s.AddItem item =
        m_biMap.AddItem (m_topHandle.Current,item)
        let retval = m_topHandle.Current
        m_topHandle.Next |> ignore
        retval
        
    member s.GetOrAddItem item =
        let success,hdl = m_biMap.TryGetKey item
        if (not success ) 
            then s.AddItem item
            else hdl
    
    member s.Item with get hdl = m_biMap.[hdl] and set (hdl,value) = m_biMap.[hdl] <- value
    
    member s.Remove hdl =
        m_biMap.Remove hdl
        
    member s.NextHandle with get = m_topHandle.Current
    interface System.Collections.IEnumerable with
        member s.GetEnumerator() = (m_biMap :> System.Collections.IEnumerable).GetEnumerator()
        
    interface System.Collections.Generic.IEnumerable<int*'a> with
        member s.GetEnumerator() = 
            (m_biMap :> System.Collections.Generic.IEnumerable<int*'a>).GetEnumerator()
            
            
            
type ISubMultiCollection<'c> = 
    abstract Add : 'c->unit
    abstract Remove : 'c ->unit
    
/// General case of the two specific cases above.  This represents a collection
/// of items with sub-collections.  Most likely the sub-collections will be
/// indexes into the main collection.
type MultiCollection<'c>() =
    let m_data = List<'c>()
    let m_multiItems = List<ISubMultiCollection<'c>>()
    
    member s.AddSubCollection idx =
        m_multiItems.Add idx
        m_data |> Seq.iter( fun item -> idx.Add item )
        
    member s.RemoveSubCollection idx =
        m_multiItems.Remove idx
        
    member s.Add value = 
        m_data.Add( value )
        m_multiItems |> Seq.iter( fun idx -> idx.Add value )
        
    member s.Remove value =
        let retval = m_data.Remove( value )
        m_multiItems |> Seq.iter( fun idx -> idx.Add value )
        retval
        
    member s.Count = m_data.Count
    
    member s.Item with get idx = m_data.[idx]
    
    member s.SubCollections = m_multiItems |> Seq.map( fun item -> item )
        
    interface System.Collections.IEnumerable with
        member s.GetEnumerator() = (m_data :> System.Collections.IEnumerable).GetEnumerator()
        
    interface System.Collections.Generic.IEnumerable<'c> with
        member s.GetEnumerator() = m_data.GetEnumerator() :> System.Collections.Generic.IEnumerator<'c>
        

/// Lets say you wanted an index into the sub collection
/// Give me a function that gets the index value (ideally unique)
/// from the collection's core items and we are in business.
type MultiCollectionIndex<'c,'d>(m_convert:'c->'d option) =
    let m_items = Dictionary<'d,'c>()
    
    member s.Items = m_items
    
    interface ISubMultiCollection<'c> with
    
        member s.Add item =
            let key = m_convert item
            match key with
                | Some key -> m_items.Add (key,item)
                | None -> ()
            
        member s.Remove item =
            let key = m_convert item
            match key with
                | Some key -> m_items.Remove key |> ignore
                | None -> ()

//Simple generic handle type so I can stop creating so many handle types...                
type Handle<'a>(m_handle:int) =
    member s.Handle = m_handle
    override s.Equals other =
        let other = other :?> Handle<'a>
        other.Handle = m_handle
    override s.GetHashCode() = m_handle
    
    
//Generic object to integer map
type ObjectEntry = { Handle:int; mutable Object:obj }

type ObjectCollection = MultiCollection<ObjectEntry>

type ObjectCollectionHandleIndex() = 
    inherit MultiCollectionIndex<ObjectEntry,int>(fun(entry)-> Some entry.Handle)
    
type ObjectCollectionObjectIndex() = 
    inherit MultiCollectionIndex<ObjectEntry,obj>(fun(entry)-> Some entry.Object)
    
    
type HandleRef = { Handle:int; mutable RefCount:int }
type HandleRefCollection = MultiCollection<HandleRef>
type HandleRefHandleIndex() = 
    inherit MultiCollectionIndex<HandleRef,int>(fun(entry)-> Some entry.Handle)
    
type NamedObjectIndex() = 
    inherit MultiCollectionIndex< ObjectEntry, string >(fun(entry) -> 
        let realEntry = entry.Object :?> INamedObject
        Some realEntry.Name );
        